/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../data_struct/head.h"
#include "../macro/head.h"
#include "parse_request_impl.h"
#include "objs.h"
#include "mgr.h"

namespace xos_http_protocol
{
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    static parse_request_impl::ITEM_DATA xos_item_data;

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    parse_request_impl::parse_request_impl()
    {
        init_data();
    }

    parse_request_impl::~parse_request_impl()
    {
        term();
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // public method

    int parse_request_impl::get_xos_item_data( ITEM_DATA*& item_ptr )
    {
        int ret = 0;
        xos_item_data.set_container( mgr::container() );
        item_ptr = &xos_item_data;
        return ret;
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int parse_request_impl::init_data()
    {
        int ret = 0;

        m_eState = STATE_NEED_MORE_HEAD_DATA;

        m_pParamProperty = 0;
        m_pPathProperty = 0;
        m_pTagProperty = 0;
        m_pRecvList = 0;
        m_pBodyList = 0;
        m_pCookieList = 0;
        m_ppSession = 0;

        m_bSSL = false;

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int parse_request_impl::init()
    {
        int ret = 0;
        return ret;
    }

    int parse_request_impl::term()
    {
        int ret = 0;
        init_data();
        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int parse_request_impl::set_param_property( xos_box::i_str_property * pProperty )
    {
        int ret = 0;
        m_pParamProperty = pProperty;
        return ret;
    }

    int parse_request_impl::set_path_property( xos_box::i_int_property * pProperty )
    {
        int ret = 0;
        m_pPathProperty = pProperty;
        return ret;
    }

    int parse_request_impl::set_tag_property( xos_box::i_str_property * pProperty )
    {
        int ret = 0;
        m_pTagProperty = pProperty;
        return ret;
    }

    int parse_request_impl::set_recv_list( xos_box::i_list * pList )
    {
        int ret = 0;
        m_pRecvList = pList;
        return ret;
    }

    int parse_request_impl::set_body_list( xos_box::i_list * pList )
    {
        int ret = 0;
        m_pBodyList = pList;
        return ret;
    }

    int parse_request_impl::set_cookie_list( xos_box::i_list * pList )
    {
        int ret = 0;
        m_pCookieList = pList;
        return ret;
    }

    int parse_request_impl::set_session( http::i_session ** ppv )
    {
        int ret = 0;
        m_ppSession = ( session_impl ** )ppv;
        return ret;
    }

    int parse_request_impl::set_ssl( bool bSSL )
    {
        int ret = 0;
        m_bSSL = bSSL;
        return ret;
    }

    int parse_request_impl::reset()
    {
        int ret = 0;
        init_data();
        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // 返回值定义：
    // 
    // 0  : 成功
    // 1  : 需要更多数据
    // -1 : 出现错误
    // 
    int parse_request_impl::proc()
    {
        int ret = 0;

        if( ( 0 == ret ) && ( STATE_NEED_MORE_HEAD_DATA == m_eState ) )
        {
            ret = parse_http_head();
        }

        if( ( 0 == ret ) && ( STATE_NEED_MORE_BODY_DATA == m_eState ) )
        {
            ret = parse_http_body();
        }

        if( 0 == ret )
        {
            parse_post_body();
        }

        if( 0 == ret )
        {
            parse_session_tag();
            parse_cookie_tag();
        }

        if( 0 == ret )
        {
            change_host_fmt();
        }

        return ret;
    }

    // 
    // www.cat.com:8080 -> www.cat.com and 8080
    // 
    int parse_request_impl::change_host_fmt()
    {
        int ret = 0;

        xos_box::i_variant * pVT = m_pTagProperty->vt( HTTP_HOST );
        xos::i_crt * pCrt = mgr::container()->crt();
        int nPort = m_bSSL ? 443 : 80;
        char host[1024] = { 0 };
        char * lpFind = 0;
        if( pVT )
        {
            pVT->str( host, sizeof( host ), 0 );
        }
        if( 0 != host[0] )
        {
            lpFind = pCrt->strchr( host, ':' );
        }
        if( lpFind )
        {
            *lpFind++ = 0;
            nPort = pCrt->atoi( lpFind );
        }
        m_pTagProperty->set( HTTP_PORT, nPort );
        pVT->set( host );

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    // 
    // 返回值定义：
    // 
    // 0  : 成功
    // 1  : 需要更多数据
    // -1 : 出现错误
    // 
    int parse_request_impl::parse_http_head()
    {
        int ret = 0;

        xos_box::i_big_buf * pBigBuf = mgr::container()->box()->big_buf();
        char * lpszData = 0;
        int head_len = 0;
        int read_len = 0;
        int nLen = 0;
        int nPos = 0;

        if( 0 == ret )
        {
            m_pRecvList->get_data( pBigBuf );
            lpszData = pBigBuf->get_data( 0, 0, 0, 0 );
            read_len = pBigBuf->get_len( 0 );
            nLen = read_len;
            nPos = 0;
        }

        if( ( 0 == ret ) && ( nLen < 8 ) )
        {
            ret = 1;
        }

        if( 0 == ret )
        {
            char * lpPost = ( char * )mgr::container()->crt()->mem_find( lpszData, nLen, "POST", 4 );
            char * lpGet = ( char * )mgr::container()->crt()->mem_find( lpszData, nLen, "GET", 3 );
            if( ( lpPost != lpszData ) && ( lpGet != lpszData ) )
            {
                ret = -1;
            }
        }

        if( 0 == ret )
        {
            char * lpEndPos = ( char * )mgr::container()->crt()->mem_find( lpszData, nLen, "\r\n\r\n", 4 );
            if( lpEndPos )
            {
                lpEndPos += 4;
                head_len = ( int )( lpEndPos - lpszData );
            }
            // 需要更多数据
            else if( nLen < 8192 )
            {
                ret = 1;
            }
            // 失败
            else
            {
                ret = -1;
            }
        }

        while( ( 0 == ret ) && ( nPos < head_len ) )
        {
            if( ( '\r' == lpszData[nPos] ) || ( '\n' == lpszData[nPos] ) )
            {
                lpszData[nPos] = 0;
            }
            nPos++;
        }

        if( 0 == ret )
        {
            nPos = 0;
        }

        if( 0 == ret )
        {
            ret = parse_cmd( lpszData, nLen, nPos );
        }

        if( 0 == ret )
        {
            ret = parse_tags( m_pTagProperty, lpszData, nLen, nPos );
        }

        if( 0 == ret )
        {
            ret = parse_url();
        }

        if( 0 == ret )
        {
            parse_params();
        }

        if( 0 == ret )
        {
            ret = parse_uri();
        }

        if( 0 == ret )
        {
            m_eState = STATE_NEED_MORE_BODY_DATA;
        }
        else if( -1 == ret )
        {
            m_eState = STATE_PROC_FAILED;
        }

        if( 0 == ret )
        {
            pBigBuf->set_len( read_len - head_len );
            pBigBuf->set_pos( head_len );
        }

        m_pRecvList->add_data( pBigBuf, false );
        xos_stl::release_interface( pBigBuf );

        return ret;
    }

    // 
    // 返回值定义：
    // 
    // 0  : 成功
    // 1  : 需要更多数据
    // -1 : 出现错误
    // 
    int parse_request_impl::parse_http_body()
    {
        int result = 1;
        int ret = 0;

        xos_box::i_buf * pBuf = 0;
        int nBodyLen = 0;
        int nRecvLen = 0;

        for( m_pBodyList->reset(); ( 0 == ret ) && ( pBuf = ( xos_box::i_buf * )m_pBodyList->next() ); )
        {
            nRecvLen += pBuf->get_len( 0 );
        }

        if( 0 == ret )
        {
            const char * lpszVal = m_pTagProperty->str( HTTP_CONTENT_LENGTH, 0, 0, 0 );
            if( lpszVal )
            {
                nBodyLen = mgr::container()->crt()->atoi( lpszVal );
            }
        }

        while( ( 0 == ret ) && ( pBuf = ( xos_box::i_buf * )m_pRecvList->front( 0 ) ) )
        {
            nRecvLen += pBuf->get_len( 0 );
            if( nRecvLen > nBodyLen )
            {
                int nRemoveLen = pBuf->get_len( 0 ) - ( nRecvLen - nBodyLen );
                xos_box::i_buf * pB = mgr::container()->box()->buf();
                pBuf->get_data( pB, nRemoveLen, 0 );
                m_pBodyList->push_back( pB );
                pBuf->move_data_to_begin();
                break;
            }
            m_pBodyList->push_back( pBuf );
            m_pRecvList->pop_front();
            if( nRecvLen == nBodyLen )
            {
                break;
            }
        }

        if( ( 0 == ret ) && ( nRecvLen >= nBodyLen ) )
        {
            result = 0;
        }

        if( 0 == result )
        {
            m_eState = STATE_PROC_SUCCESS;
        }

        return result;
    }

    // JSESSIONID: value
    int parse_request_impl::parse_session_tag()
    {
        int ret = 0;

        session_impl *& pSession = *m_ppSession;
        
        const char * lpszValue = m_pParamProperty->str( xos_http_protocol::HTTP_SESSION_ID );
        if( lpszValue )
        {
            pSession = session_impl::get( lpszValue );
            pSession->reset_timeout();
        }

        return ret;
    }

    // name=value; xxx=xxx;
    int parse_request_impl::parse_cookie_tag()
    {
        int ret = 0;

        const char * lpszValue = m_pTagProperty->str( xos_http_protocol::HTTP_COOKIE );
        if( lpszValue )
        {
            ret = cookie_impl::load( lpszValue, m_pCookieList, m_ppSession );
        }

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    // 
    // GET /abc/xyz?a=1&b=2 HTTP/1.1
    //
    int parse_request_impl::parse_cmd( char * lpszData, int & nLen, int & nPos )
    {
        int ret = 0;

        int line_len = mgr::container()->crt()->strlen( lpszData + nPos );
        char * lpszCmd = lpszData + nPos;
        char * lpszVer = 0;
        char * lpszUrl = 0;

        if( 0 == ret )
        {
            lpszUrl = mgr::container()->crt()->strchr( lpszData + nPos, ' ' );
            lpszVer = mgr::container()->crt()->strrchr( lpszData + nPos, ' ' );
            if( !lpszUrl || !lpszVer )
            {
                ret = -1;
            }
        }

        if( 0 == ret )
        {
            *lpszUrl = 0;
            *lpszVer = 0;
            lpszUrl++;
            lpszVer++;
        }

        if( 0 == ret )
        {
            ret = mgr::valid_key( lpszCmd );
        }

        if( 0 == ret )
        {
            ret = mgr::valid_uri( lpszUrl );
        }

        if( 0 == ret )
        {
            ret = mgr::valid_key( lpszVer );
        }

        if( 0 == ret )
        {
            m_pTagProperty->set( HTTP_REQUEST_CMD, lpszCmd );
            m_pTagProperty->set( HTTP_REQUEST_URL, lpszUrl );
            m_pTagProperty->set( HTTP_REQUEST_VER, lpszVer );
        }

        if( 0 == ret )
        {
            nPos = nPos + line_len + 2;
            nLen = nLen - line_len - 2;
        }

        return ret;
    }

    // 
    // host: 127.0.0.1
    //
    int parse_request_impl::parse_tags( xos_box::i_str_property * pProperty, char * lpszData, int & nLen, int & nPos )
    {
        int ret = 0;
        while( 0 == parse_tag( pProperty, lpszData, nLen, nPos ) );
        return ret;
    }

    // 
    // host: 127.0.0.1
    //
    int parse_request_impl::parse_tag( xos_box::i_str_property * pProperty, char * lpszData, int & nLen, int & nPos )
    {
        int ret = 0;

        int line_len = mgr::container()->crt()->strlen( lpszData + nPos );
        char * lpszKey = lpszData + nPos;
        char * lpszVal = 0;

        if( 0 == ret )
        {
            lpszVal = mgr::container()->crt()->strstr( lpszData + nPos, ": ", true );
            if( !lpszVal )
            {
                ret = -1;
            }
        }

        if( 0 == ret )
        {
            *lpszVal++ = 0;
            *lpszVal++ = 0;
        }

        if( 0 == ret )
        {
            ret = mgr::valid_value( lpszKey, lpszVal );
        }

        if( 0 == ret )
        {
            ret = mgr::valid_key( lpszKey );
        }

        if( 0 == ret )
        {
            pProperty->set( lpszKey, lpszVal );
        }

        if( 0 == ret )
        {
            nPos = nPos + line_len + 2;
            nLen = nLen - line_len - 2;
        }

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    // 
    // /abc/xyz?a=1&b=2
    // 
    int parse_request_impl::parse_url()
    {
        int ret = 0;

        xos_box::i_buf * pBuf = mgr::container()->box()->buf();
        char * lpszParam = 0;
        char * lpszUri = 0;

        if( 0 == ret )
        {
            lpszUri = pBuf->get_data( 0, 0, 0, 0 );
            m_pTagProperty->str( HTTP_REQUEST_URL, lpszUri, xos_box::i_buf::BUF_SIZE, 0 );
        }

        if( 0 == ret )
        {
            lpszParam = mgr::container()->crt()->strchr( lpszUri, '?' );
            if( lpszParam )
            {
                *lpszParam = 0;
                lpszParam++;
            }
        }

        if( 0 == ret )
        {
            ret = mgr::valid_uri( lpszParam );
        }

        if( 0 == ret )
        {
            ret = mgr::valid_uri( lpszUri );
        }

        if( 0 == ret )
        {
            if( lpszParam )
            {
                m_pTagProperty->set( HTTP_REQUEST_PARAM, lpszParam );
            }
            m_pTagProperty->set( HTTP_REQUEST_URI, lpszUri );
        }

        xos_stl::release_interface( pBuf );

        return ret;
    }

    // 
    // a=1&b=2
    // 
    int parse_request_impl::parse_params()
    {
        int ret = 0;

        xos_box::i_buf * pBuf = mgr::container()->box()->buf();
        char * lpszParam = 0;
        char * lpszName = 0;
        char * lpszVal = 0;
        int nLen = 0;

        if( 0 == ret )
        {
            lpszParam = pBuf->get_data( 0, 0, 0, 0 );
            const char * pRet = m_pTagProperty->str( HTTP_REQUEST_PARAM, lpszParam, xos_box::i_buf::BUF_SIZE, 0 );
            if( !pRet )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            ret = mgr::valid_uri( lpszParam );
        }

        if( 0 == ret )
        {
            nLen = mgr::container()->crt()->strlen( lpszParam );
            lpszName = lpszParam;
        }

        for( int i = 0; ( 0 == ret ) && ( i < nLen ); ++i )
        {
            if( lpszParam[i] == '=' )
            {
                lpszVal = lpszParam + i + 1;
                lpszParam[i] = 0;
            }
            else if( lpszParam[i] == '&' )
            {
                lpszParam[i] = 0;
                ret = set_param( lpszName, lpszVal );
                lpszName = lpszParam + i + 1;
            }
        }

        if( 0 == ret )
        {
            ret = set_param( lpszName, lpszVal );
        }

        xos_stl::release_interface( pBuf );

        return ret;
    }

    int parse_request_impl::set_param( const char * lpszName, const char * lpszVal )
    {
        int ret = 0;

        char value[8192] = { 0 };

        if( 0 == ret )
        {
            ret = mgr::valid_value( lpszName, lpszVal );
        }

        if( 0 == ret )
        {
            ret = mgr::valid_key( lpszName );
        }

        if( 0 == ret )
        {
            int len = mgr::container()->crt()->strlen( lpszVal );
            ret = mgr::container()->common()->misc()->url_decode( lpszVal, len, value, sizeof( value ) );
        }

        if( 0 == ret )
        {
            m_pParamProperty->set( lpszName, value );
        }

        return ret;
    }

    // 
    // /abc/xyz
    // 
    int parse_request_impl::parse_uri()
    {
        int ret = 0;

        xos_common::i_misc * pComMisc = mgr::container()->common()->misc();
        xos_box::i_buf * pBuf = mgr::container()->box()->buf();
        char * lpszUri = 0;

        if( 0 == ret )
        {
            lpszUri = pBuf->get_data( 0, 0, 0, 0 );
            const char * pRet = m_pTagProperty->str( HTTP_REQUEST_URI, lpszUri, xos_box::i_buf::BUF_SIZE, 0 );
            if( !pRet )
            {
                ret = -1;
            }
        }

        if( 0 == ret )
        {
            ret = mgr::valid_uri( lpszUri );
        }

        if( 0 == ret )
        {
            pComMisc->parse_uri_to_property( lpszUri, m_pPathProperty );
        }

        xos_stl::release_interface( pBuf );

        return ret;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    // 
    // 1.解析参数,Content-Type: application/x-www-form-urlencoded，这时先将参数放入HTTP_REQUEST_PARAM中，由parse_params与GET方式的参数一样解析
    // 2.上传文件,Content-Type: multipart/form-data
    // 
    int parse_request_impl::parse_post_body()
    {
        int result = 0;
        int ret = 0;

        const char * lpszContentType = 0;

        if( ( 0 == ret ) && ( 0 != mgr::container()->crt()->strcmp( m_pTagProperty->str( HTTP_REQUEST_CMD ), "POST" ) ) )
        {
            ret = 1;
        }

        if( 0 == ret )
        {
            lpszContentType = m_pTagProperty->str( HTTP_CONTENT_TYPE );
            if( !lpszContentType )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            if( 0 == mgr::container()->common()->misc()->begin_with( lpszContentType, POST_TYPE_APPLICATION ) )
            {
                result = application();
            }
            else if( 0 == mgr::container()->common()->misc()->begin_with( lpszContentType, POST_TYPE_MULTIPART ) )
            {
                result = multipart();
            }
            else
            {
            }
        }

        return result;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    int parse_request_impl::application()
    {
        int result = 0;
        int ret = 0;

        xos_box::i_buf * pBuf = ( xos_box::i_buf * )m_pBodyList->front( 0 );
        char * lpszData = 0;
        int nLen = 0;

        if( ( 0 == ret ) && !pBuf )
        {
            ret = 1;
        }

        if( 0 == ret )
        {
            lpszData = pBuf->get_data( 0, 0, 0, &nLen );
            lpszData[nLen] = 0;
        }

        if( 0 == ret )
        {
            ret = mgr::valid_uri( lpszData );
            result = ret;
        }

        if( ( 0 == ret ) && ( nLen > 0 ) )
        {
            m_pTagProperty->set( HTTP_REQUEST_PARAM, lpszData );
            result = parse_params();
        }

        return result;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    int parse_request_impl::multipart()
    {
        int ret = 0;

        xos_box::i_big_buf * pBigBuf = mgr::container()->box()->big_buf();

        ret = parse_multipart_body( m_pTagProperty, m_pBodyList, pBigBuf, 0, 0 );

        xos_stl::release_interface( pBigBuf );

        return ret;
    }

    /*
    Content-Type: multipart/form-data; boundary=AaB03x

    --AaB03x
    Content-Disposition: form-data; name="submit-name"

    Larry
    --AaB03x
    Content-Disposition: form-data; name="files"
    Content-Type: multipart/mixed; boundary=BbC04y

    --BbC04y
    Content-Disposition: file; filename="file1.txt"
    Content-Type: text/plain

    ... contents of file1.txt ...
    --BbC04y
    Content-Disposition: file; filename="file2.gif"
    Content-Type: image/gif
    Content-Transfer-Encoding: binary

    ...contents of file2.gif...
    --BbC04y--
    --AaB03x--
    */
    int parse_request_impl::parse_multipart_head( xos_box::i_str_property * pProperty, xos_box::i_list * pBodyList, xos_box::i_big_buf * pBigBuf, const char * lpszBound, int nBoundLen )
    {
        int ret = 0;

        char * lpszBegin = 0;
        char * lpszEnd = 0;
        char * lpszData = 0;
        int nLen = 0;
        int nPos = 0;

        if( 0 == ret )
        {
            pBodyList->get_data( pBigBuf );
            lpszData = pBigBuf->get_data( 0, 0, 0, 0 );
            nLen = pBigBuf->get_len( 0 );
            nPos = pBigBuf->get_pos( 0 );
        }

        if( 0 == ret )
        {
            lpszBegin = mgr::container()->crt()->strstr( lpszData, lpszBound, true );
            if( lpszBegin )
            {
                lpszBegin += nBoundLen;
            }
            else
            {
                ret = 1;
            }
        }

        if( ( 0 == ret ) && ( ( '-' == lpszBegin[0] ) && ( '-' == lpszBegin[1] ) ) )
        {
            nPos = nPos + nBoundLen + 2 + 2 + 2;
            nLen = nLen - nBoundLen - 2 - 2 - 2;
            pBigBuf->set_pos( nPos );
            pBigBuf->set_len( nLen );
            pBigBuf->move_data_to_begin();
            ret = 1;
        }

        if( 0 == ret )
        {
            lpszBegin += 2;
            lpszEnd = mgr::container()->crt()->strstr( lpszBegin, "\r\n\r\n", true );
            if( lpszEnd )
            {
                lpszEnd += 4;
            }
            else
            {
                ret = 1;
            }
        }

        for( int i = 0; ( 0 == ret ) && ( i < ( lpszEnd - lpszBegin ) ); ++i )
        {
            if( ( '\r' == lpszBegin[i] ) || ( '\n' == lpszBegin[i] ) )
            {
                lpszBegin[i] = 0;
            }
        }

        if( 0 == ret )
        {
            nPos += ( int )( lpszBegin - lpszData );
            nLen -= ( int )( lpszBegin - lpszData );
            ret = parse_tags( pProperty, lpszData, nLen, nPos );
            nLen -= 2;
            nPos += 2;
        }

        if( 0 == ret )
        {
            pBigBuf->set_len( nLen );
            pBigBuf->set_pos( nPos );
            pBigBuf->move_data_to_begin();
        }

        return ret;
    }

    int parse_request_impl::parse_multipart_body( xos_box::i_str_property * pProperty, xos_box::i_list * pBodyList, xos_box::i_big_buf * pBigBuf, const char * lpszBound, int nBoundLen )
    {
        int result = 1;
        int ret = 0;

        if( 0 == ret )
        {
            pBodyList->get_data( pBigBuf );
        }

        if( 0 == ret )
        {
            ret = parse_multipart_body_list( pProperty, pBodyList, pBigBuf, lpszBound, nBoundLen );
        }

        if( 0 == ret )
        {
            ret = parse_multipart_body_file( pProperty, pBodyList, pBigBuf, lpszBound, nBoundLen );
        }

        if( 0 == ret )
        {
            ret = parse_multipart_body_value( pProperty, pBodyList, pBigBuf, lpszBound, nBoundLen );
        }

        if( -1 == ret )
        {
            result = -1;
        }
        else if( 0 != ret )
        {
            result = 0;
        }

        return result;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //

    int parse_request_impl::parse_multipart_body_value( xos_box::i_str_property * pProperty, xos_box::i_list * pBodyList, xos_box::i_big_buf * pBigBuf, const char * lpszBound, int nBoundLen )
    {
        int result = 0;
        int ret = 0;

        const char * lpszName = 0;

        if( 0 == ret )
        {
            const char * lpszInfo = pProperty->str( HTTP_CONTENT_DISPOSITION );
            const char * pFind = 0;
            if( lpszInfo )
            {
                pFind = mgr::container()->crt()->strstr( lpszInfo, POST_MULTI_DISP_FORM_NAME, true );
            }
            if( pFind )
            {
                lpszName = pFind + mgr::container()->crt()->strlen( POST_MULTI_DISP_FORM_NAME );
                result = 1;
            }
            else
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            ret = mgr::valid_key( lpszName );
            result = ret;
        }

        if( 0 == ret )
        {
            char file[1024];
            // remove "filename" -> filename
            {
                mgr::container()->crt()->strcpy( file, sizeof( file ), lpszName + 1 );
                int len = mgr::container()->crt()->strlen( file );
                file[len - 1] = 0;
            }
            pProperty->set( POST_MULTI_DISP_FORM_NAME, file );
            pBodyList->get_data( pBigBuf );
            pProperty->set( MULTIPART_BODY_KEY, file );
        }

        if( 0 == ret )
        {
            const char * lpszData = pBigBuf->get_data( 0, 0, 0, 0 );
            int nLen = pBigBuf->get_len( 0 );
            int nPos = pBigBuf->get_pos( 0 );
            char * lpszEnd = ( char * )mgr::container()->crt()->mem_find( lpszData, nLen, lpszBound, nBoundLen );
            int nCopyLen = 0;
            if( lpszEnd )
            {
                *( lpszEnd - 3 ) = 0;
                *( lpszEnd - 4 ) = 0;
                nCopyLen = ( int )( lpszEnd - lpszData - 2 - 2 );
                ret = 1;
            }
            if( nCopyLen > 0 )
            {
                pProperty->set( MULTIPART_BODY_VALUE, lpszData );
                nCopyLen = nCopyLen + 2 + 2;
                nPos += nCopyLen;
                nLen -= nCopyLen;
                pBigBuf->set_len( nLen );
                pBigBuf->set_pos( nPos );
                pBigBuf->move_data_to_begin();
            }
        }

        if( 0 == ret )
        {
            const char * lpszValue = pProperty->str( MULTIPART_BODY_VALUE );
            const char * lpszKey = pProperty->str( MULTIPART_BODY_KEY );
            if( 0 == ret )
            {
                ret = mgr::valid_value( lpszKey, lpszValue );
                result = ret;
            }
            if( 0 == ret )
            {
                ret = mgr::valid_key( lpszKey );
                result = ret;
            }
            if( 0 == ret )
            {
                m_pParamProperty->set( lpszKey, lpszValue );
            }
        }

        return result;
    }

    int parse_request_impl::parse_multipart_body_file( xos_box::i_str_property * pProperty, xos_box::i_list * pBodyList, xos_box::i_big_buf * pBigBuf, const char * lpszBound, int nBoundLen )
    {
        int result = 0;
        int ret = 0;

        const char * lpszFile = 0;
        xos_box::i_list * pList = 0;

        if( 0 == ret )
        {
            const char * lpszInfo = pProperty->str( HTTP_CONTENT_DISPOSITION );
            const char * pFind = 0;
            if( lpszInfo )
            {
                pFind = mgr::container()->crt()->strstr( lpszInfo, POST_MULTI_DISP_FILE_NAME, true );
            }
            if( pFind )
            {
                lpszFile = pFind + mgr::container()->crt()->strlen( POST_MULTI_DISP_FILE_NAME );
                result = 1;
            }
            else
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            pList = mgr::container()->box()->list();
            pProperty->set_list( MULTIPART_BODY_FILE, pList, true );
            char file[1024];
            // remove "filename" -> filename
            {
                mgr::container()->crt()->strcpy( file, sizeof( file ), lpszFile + 1 );
                int len = mgr::container()->crt()->strlen( file );
                file[len - 1] = 0;
            }
            pProperty->set( POST_MULTI_DISP_FILE_NAME, file );
            pProperty->set( MULTIPART_BODY_KEY, file );
        }

        while( 0 == ret )
        {
            pBodyList->get_data( pBigBuf );
            const char * lpszData = pBigBuf->get_data( 0, 0, 0, 0 );
            int nLen = pBigBuf->get_len( 0 );
            int nPos = pBigBuf->get_pos( 0 );
            const char * lpszEnd = ( const char * )mgr::container()->crt()->mem_find( lpszData, nLen, lpszBound, nBoundLen );
            int nCopyLen = 0;
            if( lpszEnd )
            {
                nCopyLen = ( int )( lpszEnd - lpszData - 2 - 2 );
                ret = 1;
            }
            else if( nLen > nBoundLen )
            {
                nCopyLen = nLen - nBoundLen;
            }
            if( nCopyLen > 0 )
            {
                pBigBuf->set_len( nCopyLen );
                pList->add_data( pBigBuf, true );
                nCopyLen = nCopyLen + 2 + 2;
                nPos += nCopyLen;
                nLen -= nCopyLen;
                pBigBuf->set_pos( nPos );
                pBigBuf->set_len( nLen );
                pBigBuf->move_data_to_begin();
            }
        }

        if( 0 == ret )
        {
            const char * lpszKey = pProperty->str( MULTIPART_BODY_KEY );
            xos_box::i_list * pList = pProperty->list( MULTIPART_BODY_FILE );
            pProperty->set_list( MULTIPART_BODY_FILE, pList, false );
            m_pParamProperty->set_list( lpszKey, pList, true );
        }

        return result;
    }

    int parse_request_impl::parse_multipart_body_list( xos_box::i_str_property * pProperty, xos_box::i_list * pBodyList, xos_box::i_big_buf * pBigBuf, const char * lpszBound, int nBoundLen )
    {
        int result = 0;
        int ret = 0;

        xos_box::i_str_property * pBodyProperty = 0;
        const char * lpszBoundary = 0;
        int nBoundaryLen = 0;
        const char * lpszName = 0;

        if( 0 == ret )
        {
            const char * lpszContentType = pProperty->str( HTTP_CONTENT_TYPE );
            const char * pFind = 0;
            if( lpszContentType )
            {
                pFind = mgr::container()->crt()->strstr( lpszContentType, POST_MULTI_BOUNDARY, true );
            }
            if( pFind )
            {
                lpszBoundary = pFind + mgr::container()->crt()->strlen( POST_MULTI_BOUNDARY );
                nBoundaryLen = mgr::container()->crt()->strlen( lpszBoundary );
                result = 1;
            }
            else
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            const char * lpszInfo = pProperty->str( HTTP_CONTENT_DISPOSITION );
            const char * pFind = 0;
            if( lpszInfo )
            {
                pFind = mgr::container()->crt()->strstr( lpszInfo, POST_MULTI_DISP_FORM_NAME, true );
            }
            if( pFind )
            {
                lpszName = pFind + mgr::container()->crt()->strlen( POST_MULTI_DISP_FORM_NAME );
            }
            if( !lpszName )
            {
                lpszName = "\"top_level\"";
            }
        }

        if( 0 == ret )
        {
            char file[1024];
            // remove "filename" -> filename
            {
                mgr::container()->crt()->strcpy( file, sizeof( file ), lpszName + 1 );
                int len = mgr::container()->crt()->strlen( file );
                file[len - 1] = 0;
            }
            pBodyProperty = mgr::container()->box()->get_str_prop();
            pProperty->set_str_prop( MULTIPART_BODY_LIST, pBodyProperty, true );
            pProperty->set( MULTIPART_BODY_KEY, file );
        }

        while( 0 == ret )
        {
            pBodyList->get_data( pBigBuf );
            xos_box::i_str_property * pProp = 0;
            pProp = mgr::container()->box()->get_str_prop();
            ret = parse_multipart_head( pProp, pBodyList, pBigBuf, lpszBoundary, nBoundaryLen );
            if( 0 == ret )
            {
                ret = parse_multipart_body( pProp, pBodyList, pBigBuf, lpszBoundary, nBoundaryLen );
            }
            if( 0 == ret )
            {
                const char * lpszKey = pProp->str( MULTIPART_BODY_KEY );
                pBodyProperty->set_str_prop( lpszKey, pProp, true );
                pProp = 0;
            }
            xos_stl::release_interface( pProp );
        }

        return result;
    }

} // xos_http_protocol
